/*
 * Copyright 2011 Focus Technology, Co., Ltd. All rights reserved.
 */
package com.tfc.analysis.utils;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.tfc.analysis.entity.Keyword;
import com.tfc.analysis.entity.StopCharacter;
import com.tfc.analysis.fragment.ReplaceWithExceptFragment;
import com.tfc.analysis.process.Highlight;
import com.tfc.analysis.process.WordFinder;

/**
 * 关键词分析工具类。
 *
 * @author taofucheng
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class AnalysisUtils {
    private static WordFinder wordFinder = new WordFinder();
    private static Highlight highlight = new Highlight();
    private static ReplaceWithExceptFragment empty = new ReplaceWithExceptFragment("");

    /**
     * 构建关键词
     *
     * @param word   词语
     * @param endTag 属性信息
     * @return
     */
    public static Keyword getKeyword(String word, Map<String, Object> endTag) {
        Keyword tmp = new Keyword(word);
        Map<String, Object> props = (Map<String, Object>) endTag.get(Keyword.propsName);
        if (props != null && !props.isEmpty()) {
            tmp.getAllProps().putAll(props);
        }
        return tmp;
    }

    /**
     * 将指定的词构造到一棵树中。
     *
     * @param tree    构造出来的树
     * @param word    指定的词
     * @param keyword 对应的词
     * @return
     */
    public static Map<String, Map> makeTreeByWord(Map<String, Map> tree, String word, Keyword keyword) {
        word = StringUtils.trimToEmpty(word);
        if (StringUtils.isEmpty(word)) {
            tree.put(StopCharacter.TREE_END_TAG, getAttrMap(keyword));
            return tree;
        }
        String next = word.substring(0, 1);
        Map w = tree.get(next);
        if (w == null) {
            w = new HashMap<String, Map>();
        }
        tree.put(next, makeTreeByWord(w, word.substring(1), keyword));
        return tree;
    }

    /**
     * 获取关键词的一些属性
     *
     * @param word 关键词
     * @return
     */
    private static Map getAttrMap(Keyword word) {
        Map<String, Object> attribute = new HashMap<String, Object>();
        attribute.put(Keyword.propsName, word.getAllProps());
        return attribute;
    }

    /**
     * 根据精确、模糊等匹配方式返回相应的实际关键词。
     *
     * @param tmp   临时词
     * @param pre   前缀
     * @param sufix 后缀
     * @return 返回设置好的关键词
     */
    private static Keyword checkPattern(Keyword tmp, String pre, String sufix) {
        tmp.setBefore(pre);
        tmp.setAfter(sufix);
        return tmp;
    }

    /**
     * 查询文本开头的词是否在词库树中，如果在，则返回对应的词，如果不在，则返回null。
     *
     * @param matchedContent 已经匹配上的内容，暂存
     * @param pre            词的前一个字，如果为空，则表示前面没有内容
     * @param nextWordsTree  下一层树
     * @param text           剩余的文本内容
     * @param ignoreTree     忽略内容的词树结构
     * @return 返回找到的词
     */
    public static Keyword getSensitiveWord(String matchedContent, String pre, Map<String, Map> nextWordsTree, String text, Map<String, Map> ignoreTree, int ignoreWordMaxLen) {
        if (nextWordsTree == null || nextWordsTree.isEmpty()) {
            return null;
        }
        Map<String, Object> endTag = nextWordsTree.get(StopCharacter.TREE_END_TAG);
        if (StringUtils.isEmpty(text)) {
            if (endTag != null) {
                // 如果有结束符，则表示匹配成功
                return checkPattern(getKeyword(matchedContent, endTag), pre, null);
            } else {
                // 没有，则返回null
                return null;
            }
        }
        // 过滤掉忽略内容，取第一个非忽略内容
        text = cutStartIgnore(text, ignoreTree, ignoreWordMaxLen);
        String next = text.substring(0, 1);
        String suffix = null;
        if (text.length() > 0) {
            suffix = text.substring(0, 1);
        }
        Map<String, Map> nextTree = nextWordsTree.get(next);
        if (endTag == null) {
            if (nextTree != null && nextTree.size() > 0) {
                // 没有结束标志，则表示关键词没有结束，继续往下走。
                return getSensitiveWord(matchedContent + next, pre, nextTree, text.substring(1), ignoreTree, ignoreWordMaxLen);
            } else {
                // 如果没有下一个匹配的字，表示匹配结束！
                return null;
            }
        } else {
            Keyword tmp = null;
            if (nextTree != null && nextTree.size() > 0) {
                // 如果大于0，则表示还有更长的词，继续往下找
                tmp = getSensitiveWord(matchedContent + next, pre, nextTree, text.substring(1), ignoreTree, ignoreWordMaxLen);
                if (tmp == null) {
                    // 没有更长的词，则就返回这个词。在返回之前，先判断它是模糊的，还是精确的
                    tmp = getKeyword(matchedContent, endTag);
                }
                return checkPattern(tmp, pre, suffix);
            } else {
                // 没有往下的词了，那就是关键词结束了。
                return checkPattern(getKeyword(matchedContent, endTag), pre, suffix);
            }
        }
    }

    /**
     * 判断是否有忽略的内容为开头，如果有，则将其去除
     *
     * @param text             待匹配的内容
     * @param ignoreTree       忽略词
     * @param ignoreWordMaxLen 忽略内容的最长的词组合，用于性能改善
     * @return
     */
    public static String cutStartIgnore(String text, Map<String, Map> ignoreTree, int ignoreWordMaxLen) {
        while (true) {
            if (ignoreTree == null || ignoreTree.isEmpty() || StringUtils.isEmpty(text)) {
                return text;
            }
            if (ignoreWordMaxLen > text.length()) {
                ignoreWordMaxLen = text.length();
            }
            String first = text.substring(0, 1);
            Map n = ignoreTree.get(first);
            if (n == null || n.isEmpty()) {
                return text;//根本就没有，说明不需要后面的查找了
            }
            Set<String> words = wordFinder.process(ignoreTree, text.substring(0, ignoreWordMaxLen), null, 1);
            if (words == null || words.isEmpty()) {
                return text;
            }
            boolean matched = false;
            for (String w : words) {
                if (text.startsWith(w)) {
                    //如果是这个开头，则认为是匹配上了，直接处理
                    text = text.substring(w.length());
                    matched = true;
                    break;
                }
            }
            if (!matched) {//没有匹配上，说明已经结束了
                return text;
            }
        }
    }

    /**
     * 判断查询到的词是否是白名单的词
     *
     * @param w               匹配到的词
     * @param wordToWhite     白名单词库
     * @param whiteWordMaxLen 白名单中最长的词的长度
     * @param pre             该词前面的内容，即已经处理过的内容
     * @param suffix          该词后面的内容，即：等待匹配的内容
     * @param ignoreTree      忽略内容的词树结构
     * @return
     */
    public static boolean isWhiteWord(Keyword w, Map<Keyword, Set<Keyword>> wordToWhite, int whiteWordMaxLen, StringBuilder pre, String suffix, Map<String, Map> ignoreTree) {
        if (wordToWhite == null || wordToWhite.isEmpty()) {
            //如果没有白名单词，则查到的词就是要匹配的词，即：非白名单
            return false;
        }
        if (whiteWordMaxLen < w.getWordLength()) {
            return false;//如果白名单最长的词都比查到的词短，那就没有对比的必要
        }
        //选用白名单词树搜索方式，会有误判的情况，如：吸毒是敏感词，其前后内容为：“一次性交30元有吸毒的入门资格”，其中“一次性”是白名单词，这时候会误判该敏感词是非敏感词
        Set<Keyword> whites = wordToWhite.get(w);
        if (whites == null || whites.isEmpty()) {
            return false;//没有白名单
        }
        //拼接出白名单中最大长度的词的内容，然后使用白名单进行匹配操作，将查到的词进行判断
        StringBuilder segment = new StringBuilder();
        int interval = whiteWordMaxLen - w.getWordLength();
        // 过滤掉忽略内容，取第一个非忽略内容
        if (interval > 0) {
            StringBuilder tmp = new StringBuilder("");
            StringBuilder orig = new StringBuilder("");
            orig.append(pre);
            while (tmp.length() < interval && orig.length() > 0) {
                String s = "";
                if (orig.length() <= interval) {
                    s = orig.toString();
                    orig.delete(0, orig.length());
                } else {
                    s = orig.substring(orig.length() - interval);
                    orig.delete(orig.length() - interval, orig.length());
                }
                s = highlight.process(ignoreTree, s, empty, 1);
                tmp.insert(0, s);//向前插入
            }
            if (tmp.length() > interval) {
                segment.append(tmp.substring(tmp.length() - interval));
            } else {
                segment.append(tmp);
            }
        }
        segment.append(w.getWord());
        if (interval > 0) {
            StringBuilder tmp = new StringBuilder();
            while (tmp.length() < interval && suffix.length() > 0) {
                int start = w.getWordLength() - 1;
                int end = w.getWordLength() - 1 + interval;
                if (start > suffix.length()) {
                    start = suffix.length();
                }
                if (end > suffix.length()) {
                    end = suffix.length();
                }
                tmp.append(highlight.process(ignoreTree, suffix.substring(start, end), empty, 1));
                suffix = suffix.substring(end);
            }
            if (tmp.length() > interval) {
                tmp.delete(interval, tmp.length());
            }
            segment.append(tmp);
        }
        for (Keyword k : whites) {
            if (segment.indexOf(k.getWord()) > -1) {
                return true;
            }
        }
        return false;
    }
}
